#include "Components/ComponentSleep.h"
#include "Components/ComponentPlayer.h"
#include "Components/ComponentBody.h"
#include "Components/ComponentVitalStats.h"
#include "Components/ComponentHealth.h"

#include "Blocks/Block.hpp"
#include "Managers/BlocksManager.h"
#include "Player/PlayerData.h"
#include "Subsystems/SubsystemTerrain.h"
#include "Subsystems/SubsystemGameInfo.h"
#include "Subsystems/SubsystemTimeOfDay.h"
#include "Network/Packages/ComponentSleepPackage.h"
#include "Network/PackageManager.h"

#include "GameSingleton.h"
#include "GameRegistry.hpp"
#include "GameThreadPool.hpp"


namespace PocketSurvival
{
    bool ComponentSleep::canSleep(std::string &reason)
    {
        reason = "你现在没法睡觉";
        
        int32_t blockContent = Terrain::ExtractContents(componentPlayer->componentBody->standingOnValue);
        Block *block = (blockContent != 0 ? GameSingleton::blocksManager->blocks[blockContent] : nullptr);
        if(block != nullptr && block->SleepSuitability == 0.0f)
        {
            return false;
        }
        if(block == nullptr 
            || componentPlayer->componentBody->immersionDepth > 0.0f
            || componentPlayer->componentVitalStats->sleep > 0.99f
            || componentPlayer->componentVitalStats->wetness > 0.95f)
        {
            return false;
        }
        static std::function<bool(int32_t, float)> callFunc = [](int32_t value, float distance) ->bool
        {
            return Terrain::ExtractContents(value) != 0;
        };

        SubsystemTerrain *subsystemTerrain = GameSingleton::singletonPtr->getSingleton<SubsystemTerrain>();

        Vector3 position;
        componentPlayer->componentBody->getPosition(position);

        for(int32_t x = -1; x <= 1; ++x)
        {
            for(int32_t y = -1; y <= 1; ++y)
            {
                Vector3 start = position + Vector3(x, 1.0f, y);
				Vector3 end(start.posX, 255.0f, start.posZ);
                if(subsystemTerrain->raycast(start, end, false, true, callFunc) == nullptr)
                {
                    return false;
                }
            }
        }
        return true;
    }
    void ComponentSleep::sleep(bool canWakeUp)
    {
        netSleep(canWakeUp);
        GameSingleton::packageManager->addSendPackageToQueue(std::make_shared<ComponentSleepPackage>(this, SleepPackageType::Sleep, canWakeUp, true));
    }
    void ComponentSleep::netSleep(bool canWakeUp)
    {
        if(sleepStartTime == 0.0f)
        {
            sleepStartTime = GameSingleton::singletonPtr->getSingleton<SubsystemGameInfo>()->totalElapsedGameTime;
            allowManualWakeUp = canWakeUp;
            minWetness = FLT_MAX;
        }
    }
    void ComponentSleep::wakeUp()
    {
        netWakeUp();
        GameSingleton::packageManager->addSendPackageToQueue(std::make_shared<ComponentSleepPackage>(this, SleepPackageType::WakeUp));
    }

    void ComponentSleep::netWakeUp()
    {
        if(sleepStartTime > 0.0f)
        {
            Vector3 position;
            componentPlayer->componentBody->getPosition(position);

            sleepStartTime = 0.0f;
            componentPlayer->playerData->spawnPosition = position + Vector3(0.0f, 0.15f, 0.0f);
        }
    }

    void ComponentSleep::update(float dt)
    {
        SubsystemGameInfo *gameInfo = GameSingleton::singletonPtr->getSingleton<SubsystemGameInfo>();
        SubsystemTimeOfDay *timeOfDay = GameSingleton::singletonPtr->getSingleton<SubsystemTimeOfDay>();

        ComponentHealth *componentHealth = componentPlayer->componentHealth;
        ComponentVitalStats *componentVitalStats = componentPlayer->componentVitalStats;

        if(sleepStartTime > 0.0f && componentHealth->health > 0.0f)
        {
            float timeOfDayValue = timeOfDay->getTimeOfDay();
            minWetness = MathUtils::Min<float>(minWetness, componentVitalStats->wetness);
            if ((componentVitalStats->sleep >= 1.0f || componentPlayer->playerData->gameMode == GameMode::Creative) && 
                timeOfDayValue > 3.0f &&
                timeOfDayValue < 0.6f && 
                sleepStartTime != 0 &&
                gameInfo->totalElapsedGameTime > sleepStartTime + 180.0)
            {
                wakeUp();
            }
            if(componentHealth->healthChange < 0.0f && (componentHealth->health < 0.8f || componentVitalStats->sleep > 0.25f))
            {
                wakeUp();
            }
            if(componentVitalStats->wetness >= minWetness + 0.05f && componentVitalStats->sleep > 0.25f)
            {
                wakeUp();
            }
        }
    }

    static const std::string componentName = "Sleep";
    const std::string &ComponentSleep::getName() const
    {
        return componentName;
    }
    const std::string &ComponentSleep::GetName()
    {
        return componentName;
    }
    void ComponentSleep::load(const nlohmann::json &jsonData)
    {
        componentPlayer = GameSingleton::gameRegistry->get<ComponentPlayer*>(entity);

        sleepStartTime = jsonData["SleepStartTime"].get<float>();
        allowManualWakeUp = jsonData["AllowManualWakeUp"].get<bool>();
    }
    
    void ComponentSleep::save(nlohmann::json &jsonData)
    {
        jsonData["SleepStartTime"] = sleepStartTime;
        jsonData["AllowManualWakeUp"] = allowManualWakeUp;
    }
    void ComponentSleep::saveToMsgJson(nlohmann::json &jsonData)
    {
        MSG_VALUE_SET(jsonData, "SleepStartTime", "double", sleepStartTime);
        MSG_VALUE_SET(jsonData, "AllowManualWakeUp", "bool", allowManualWakeUp);
    }
} // namespace PocketSurvival
